/*
 * Matthew Cramer, Jesse Martinez, Marc Mendoza 
 * CSE 325 Lab 2
 * File: ProcessManager.c
 * Description: Manager that handles the Queue interactions and processes.  
 *              This is called from the testing interface to perform 
 *              transitions to the queues.
 *
 */

#include "QueueManager.h"
#define MAX_PROCESSES 20
#define MAX_QUEUE_SIZE 21

process_control_block tempQueue[1];

int totalProcesses = 0;

//Create New queue and its head and tail pointers
process_control_block new[21];
int newHead = -1;
int newTail = -1;
int newFreeHead = 0;
int newFreeTail = MAX_QUEUE_SIZE-1;
int newSize = 0;

//Create Ready queue and its head and tail pointers
process_control_block ready[21];
int readyHead = -1;
int readyTail = -1;
int readyFreeHead = 0;
int readyFreeTail = MAX_QUEUE_SIZE-1;
int readySize = 0;

//Create Running queue and its head and tail pointers
process_control_block running[21];
int runningHead = -1;
int runningTail = -1;
int runningFreeHead = 0;
int runningFreeTail = MAX_QUEUE_SIZE-1;
int runningSize=0;

//Create Wait queue and its head and tail pointers
process_control_block wait[21];
int waitHead = -1;
int waitTail = -1;
int waitFreeHead = 0;
int waitFreeTail = MAX_QUEUE_SIZE-1;
int waitSize = 0;

//Create Terminate queue and its head and tail pointers
process_control_block terminate[21];
int terminateHead = -1;
int terminateTail = -1;
int terminateFreeHead = 0;
int terminateFreeTail = MAX_QUEUE_SIZE-1;
int terminateSize = 0;

process_control_block* getQueue(int);
int getHead(int);
int getTail(int);
int toNew(int, int, int, int, int, int);
void toReady();
void goFunction();
int unwaitFunction(int);
int eoquantumFunction();
int eolifeFunction();
int waitFunction();
int processManagerFunction(int, int);
void initializeQueues();
void clearQueues();

//Initializes Free Lists for queues
void initializeQueues()
{
    iniFree(&newFreeHead,&newFreeTail, new);
    iniFree(&readyFreeHead, &readyFreeTail, ready);
	iniFree(&runningFreeHead, &runningFreeTail, running);
    iniFree(&waitFreeHead, &waitFreeTail, wait);
    iniFree(&terminateFreeHead, &terminateFreeTail, terminate);
}

//Initializes Queues for testing purposes
void clearQueues()
{
    iniQueue(&newHead, &newTail, &newFreeHead, &newFreeTail, &newSize, new);
    iniQueue(&readyHead, &readyTail, &readyFreeHead, &readyFreeTail, &readySize, ready);
    iniQueue(&runningHead, &runningTail, &runningFreeHead, &runningFreeTail, &runningSize, running);
    iniQueue(&waitHead, &waitTail, &waitFreeHead, &waitFreeTail, &waitSize, wait);
    iniQueue(&terminateHead, &terminateTail, &terminateFreeHead, &terminateFreeTail, &terminateSize, terminate);
	initializeQueues();
}

//Return queue to Testing Interface
process_control_block* getQueue(int queue)
{
    if(queue==1)
    {
        return new;
    }
    else if(queue==2)
    {
        return ready;
    }
    else if(queue==3)
    {
        return running;
    }
    else if(queue==4)
    {
        return wait;
    }
    else if(queue==5)
    {
        return terminate;
    }
}

//Return head pointer for queue to Testing Interface
int getHead(int queue)
{
    if(queue==1)
    {
        return newHead;
    }
    else if(queue==2)
    {
        return readyHead;
    }
    else if(queue==3)
    {
        return runningHead;
    }
    else if(queue==4)
    {
        return waitHead;
    }
    else if(queue==5)
    {
        return terminateHead;
    }
}

//Return tail pointer for queue to Testing Interface
int getTail(int queue)
{
    if(queue==1)
    {
        return newTail;
    }
    else if(queue==2)
    {
        return readyTail;
    }
    else if(queue==3)
    {
        return runningTail;
    }
    else if(queue==4)
    {
        return waitTail;
    }
    else if(queue==5)
    {
        return terminateTail;
    }
}

//This function will enqueue new process to new queue
int toNew(int pid, int psw, int page, int reg0, int reg1, int reg2)
{
    int returnValue = 0;
    //If max processes in system hit, return error
    if(totalProcesses == MAX_PROCESSES)
    {
        returnValue = -8;
    }
    else
    {
        //If new queue is not full, enqueue
        if(newSize < MAX_QUEUE_SIZE)
        {
            Enqueue(pid,psw,page,reg0,reg1,reg2,&newHead, &newTail, 
                    &newFreeHead, &newFreeTail, new);
            newSize++;
            totalProcesses++;
        }
        //If new queue is full, return error
        else
        {
            returnValue = -1;
        }
    }
    
    return returnValue;
}

//This function will move a process from the new queue to the ready queue
void toReady()
{
    tempQueue[0] = Dequeue(&newHead, &newTail,&newFreeHead, &newFreeTail, new);
    newSize--;
    
    Enqueue(tempQueue[0].pid, tempQueue[0].psw, tempQueue[0].pageTable, tempQueue[0].regs[0],
            tempQueue[0].regs[1], tempQueue[0].regs[2], &readyHead, &readyTail, 
            &readyFreeHead, &readyFreeTail, ready);
    readySize++;
}

void goFunction(int action)
{
    //If process needs to be put in ready queue from new queue
    if(action == 0)
    {
        tempQueue[0] = Dequeue(&newHead, &newTail,&newFreeHead, &newFreeTail, new);
        newSize--;
        Enqueue(tempQueue[0].pid, tempQueue[0].psw, tempQueue[0].pageTable, tempQueue[0].regs[0],
                tempQueue[0].regs[1], tempQueue[0].regs[2], &readyHead, &readyTail, 
                &readyFreeHead, &readyFreeTail, ready);   
        readySize++;
    }
    //If process needs to be put in running state
    else if(action == 1)
    {
        tempQueue[0] = Dequeue(&readyHead, &readyTail, &readyFreeHead, &readyFreeTail, ready);
        readySize--;
        Enqueue(tempQueue[0].pid, tempQueue[0].psw, tempQueue[0].pageTable, tempQueue[0].regs[0],
                tempQueue[0].regs[1], tempQueue[0].regs[2], &runningHead, &runningTail, 
                &runningFreeHead, &runningFreeTail, running);   
        runningSize=1;
    }
    //If process in running needs to be put in ready
    else if(action == 2)
    {
        tempQueue[0] = Dequeue(&runningHead, &runningTail, &runningFreeHead, &runningFreeTail, running);
        runningSize=0;
        Enqueue(tempQueue[0].pid, tempQueue[0].psw, tempQueue[0].pageTable, tempQueue[0].regs[0],
                tempQueue[0].regs[1], tempQueue[0].regs[2], &readyHead, &readyTail, 
                &readyFreeHead, &readyFreeTail, ready);   
        readySize++;
    }
}

//This function will move a process from waiting queue to ready queue
int unwaitFunction(int PID)
{
    int returnValue=0;
    tempQueue[0] = Delete(PID, &waitHead, &waitTail, &waitFreeTail, wait, tempQueue);
    if(tempQueue[0].pid == -2)
    {
        returnValue = -2;
    }
    else
    {
        waitSize--;
        Enqueue(tempQueue[0].pid, tempQueue[0].psw, tempQueue[0].pageTable, tempQueue[0].regs[0],
                tempQueue[0].regs[1], tempQueue[0].regs[2], &readyHead, &readyTail, 
                &readyFreeHead, &readyFreeTail, ready);
        readySize++;
    }
    /*****
     * Return -2 if Process is not found
     * Else Return Process ID
     */
    return returnValue;
}

//This function will move a process from running queue to ready queue
int eoquantumFunction()
{
    tempQueue[0] = Dequeue(&runningHead, &runningTail, &runningFreeHead, &runningFreeTail, running);
    runningSize=0;
    Enqueue(tempQueue[0].pid, tempQueue[0].psw, tempQueue[0].pageTable, tempQueue[0].regs[0],
            tempQueue[0].regs[1], tempQueue[0].regs[2], &readyHead, &readyTail, 
            &readyFreeHead, &readyFreeTail, ready);
    readySize++;
    return 0;
}

//This function will move a process from running queue to terminated queue
int eolifeFunction()
{
    tempQueue[0] = Dequeue(&runningHead, &runningTail, &runningFreeHead, &runningFreeTail, running);
    runningSize=0;
    Enqueue(tempQueue[0].pid, tempQueue[0].psw, tempQueue[0].pageTable, tempQueue[0].regs[0],
            tempQueue[0].regs[1], tempQueue[0].regs[2], &terminateHead, &terminateTail, 
            &terminateFreeHead, &terminateFreeTail, terminate);
    terminateSize++;
    return 0;
}

//This function will move a process from running queue to waiting queue
int waitFunction()
{
    tempQueue[0] = Dequeue(&runningHead, &runningTail, &runningFreeHead, &runningFreeTail, running);
    runningSize=0;
    Enqueue(tempQueue[0].pid, tempQueue[0].psw, tempQueue[0].pageTable, tempQueue[0].regs[0],
            tempQueue[0].regs[1], tempQueue[0].regs[2], &waitHead, &waitTail, 
            &waitFreeHead, &waitFreeTail, wait);
    waitSize++;
    return 0;
}

//This function wil receive the commands from the testing interface
//Returns a PID that has been deleted to be reused 
int processManagerFunction(int command, int PID)
{
    int returnValue = 0;
    
    //Determine what the transition is from Testing Interface
    //0=GO
    //1=UNWAIT
    //2=EOQUANTUM
    //3=EOLIFE
    //4=WAIT
    //GO Command: Moves running process to ready and a ready process to running
    if(command == 0)
    {
        if(readySize==0 && runningSize==0 && newSize==0)
        {   
            returnValue = -7;
        }
        else
        {
            //Move process from New to Ready
            if(newSize>0 && readySize<MAX_QUEUE_SIZE)
            {
                goFunction(0);
            }
            //Move Process from Running to Ready
            if(readySize<MAX_QUEUE_SIZE && runningSize==1)
            {
                goFunction(2);
            }
            //If there is nothing in the running queue, add it from ready
            if(runningSize==0 && readySize>0)
            {
                goFunction(1);
            }
        }
    }
    //UNWAIT Command: move process from wait queue to ready queue
    else if(command == 1)
    {
        if(readySize < MAX_QUEUE_SIZE && waitSize>0)
        {
            returnValue = unwaitFunction(PID);
        }
        else
        {
            /*****
             * Return -3 if Process can't be moved from Wait to Ready
             * Else Return Process ID
             */
            returnValue = -3;
        }
    }
    //EOQUANTUM Command: move process from running queue to ready queue
    else if(command == 2)
    {
        if(readySize < MAX_QUEUE_SIZE && runningSize == 1)
        {
            returnValue = eoquantumFunction();
        }
        else
        {
            /*****
             * Return -4 if Process can't be moved from Running to Ready
             * Else Return Process ID
             */
            returnValue = -4;
        }
    }
    //EOLIFE Command: Move process from Running queue to Terminate queue
    else if(command == 3)
    {
        if(runningSize == 1)
        {
            returnValue = eolifeFunction();
        }
        else
        {
            /*****
             * Return -5 if Process can't be moved from Running to Terminate
             * Else Return Process ID
             */
            return -5;
        }
    }
    //WAIT Command: Move process from running queue to wait Queue
    else if(command == 4)
    {
        if(runningSize == 1 && waitSize < MAX_QUEUE_SIZE)
        {
            returnValue = waitFunction();
        }
        else
        {
            /*****
             * Return -6 if Process can't be moved from running to Wait
             * Else Return Process ID
             */
            returnValue = -6;
        }
    }
    
    //Determine if there are elements to delete, if yes, delete them
    if(terminateSize==1)
    {
        tempQueue[0] = Dequeue(&terminateHead, &terminateTail, &terminateFreeHead, &terminateFreeTail, terminate);
        //Return the PID that was just deleted, this is > 0
        returnValue = tempQueue[0].pid;
        terminateSize--;
        totalProcesses--;
    }
    
    return returnValue;
}

